<?php 
-------------------------------------------------------
Chỉ SELECT những trường mà bạn cần

-------------------------------------------------------
Giải phóng bộ nhớ ngay sau khi sử dụng xong, mysql_free_result()

-------------------------------------------------------
Chọn đúng Engine cho bảng
Nếu SELECT nhiều hơn, bạn nên sử dụng MyISAM. (table lock)
Nếu INSERT/UPDATE/DELETE nhiều hơn bạn nên chuyển sang InnoDB (row lock)

-------------------------------------------------------
Chọn đúng type cho trường

INT, MEDIUMINT, SMALLINT, TINYINT
DATE, DATETIME

-------------------------------------------------------
sử dụng PROCEDURE ANALYSE()

Cho phép phân tích cấu trúc cột (với dữ liệu thực tế)
Ví dụ, nếu bạn tạo ra một trường INT cho khóa chính của bạn, tuy nhiên không có quá nhiều hàng, nó có thể đề nghị bạn sử dụng MEDIUMINT thay thế.
Hoặc nếu bạn đang sử dụng một lĩnh vực VARCHAR, bạn có thể nhận được một đề nghị để chuyển đổi nó để ENUM, nếu chỉ có vài giá trị duy nhất.

trong phpMyAdmin có thẻ click nhanh Propose table structure để chạy

-------------------------------------------------------
Chấp nhận dư thừa dữ liệu

-------------------------------------------------------
Tạo view để hỗ trợ cho việc join bảng

-------------------------------------------------------
Phân vùng dọc

Phân vùng dọc là việc tách cấu trúc bảng của bạn theo chiều dọc vì lý do tối ưu hóa.

Ví dụ 1: Bạn có thể có một bảng người sử dụng có chứa các địa chỉ nhà, không được đọc thường xuyên.
Bạn có thể chọn để chia bảng của bạn và lưu trữ các thông tin địa chỉ trên một bảng riêng biệt.
Bằng cách này, bảng người sử dụng chính của bạn sẽ thu nhỏ kích thước.
Như bạn đã biết, bảng nhỏ hơn thực hiện nhanh hơn.

Ví dụ 2: Bạn có một lĩnh vực "last_login" trong bảng.
Nó cập nhật mỗi khi người dùng đăng nhập vào trang web.
Nhưng tất cả các bản cập nhật trên bàn làm cho bộ nhớ cache truy vấn cho rằng bảng sẽ được xóa.
Bạn có thể đặt lĩnh vực đó vào một bảng khác để tiếp tục cập nhật bảng người sử dụng của bạn ở mức tối thiểu.

-------------------------------------------------------
Index và Fulltext search

Đánh chỉ mục không chỉ cho primary keys hay unique keys mà cả các trường mà bạn sử dụng để tìm kiếm (username)
SELECT *
FROM `users`
WHERE `username` LIKE '%khanh%'

Đánh chỉ mục cho các trường liên kết (group_id)
SELECT username, name
FROM users u
JOIN groups g ON u.group_id = g.id
WHERE g.id

-------------------------------------------------------
dùng EXPLAIN để phân tích câu query

Sử dụng từ khóa EXPLAIN có thể cung cấp cho bạn cái nhìn sâu sắc về những gì MySQL đang làm gì để thực hiện truy vấn của bạn.

-------------------------------------------------------
LIMIT 1 When Getting a Unique Row
Nếu bạn biết bạn đang tìm kiếm chỉ là một hàng hãy thêm LIMIT 1.
$r = mysql_query("SELECT * FROM user WHERE state = 'HaNoi'");

$r = mysql_query("SELECT 1 FROM user WHERE state = 'HaNoi' LIMIT 1");

Bằng cách này, cơ sở dữ liệu sẽ ngừng quá trình quét các bản ghi sau khi nó tìm thấy 1 bản ghi bất kỳ

-------------------------------------------------------
Do Not ORDER BY RAND()

$r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");


$r = mysql_query("SELECT count(*) FROM user");
$d = mysql_fetch_row($r);
$rand = mt_rand(0,$d[0] - 1);
$r = mysql_query("SELECT username FROM user LIMIT $rand, 1");

-------------------------------------------------------
Optimize Your Queries For the Query Cache
Hầu hết các máy chủ MySQL có truy vấn bộ nhớ đệm được kích hoạt.
Khi cùng một truy vấn được thực hiện nhiều lần, kết quả được lấy từ bộ nhớ cache

// query cache does NOT work
$r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");

// query cache works!
$today = date("Y-m-d");
$r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

Kể từ khi kết quả trả lại của hàm (CURDATE(), NOW(), RAND()) có thể thay đổi,
MySQL quyết định vô hiệu hóa truy vấn bộ nhớ đệm cho truy vấn đó.



























-------------------------------------------------------
Use ENUM over VARCHAR

Ví dụ bạn có trường status và chỉ gồm 4 giái trị "active", "inactive", "pending", "expired",
Hãy sử dụng ENUM vì chúng rất nhanh và gọn

-------------------------------------------------------
Use NOT NULL If You Can

bạn nên luôn luôn đặt các cột của bạn là NOT NULL.

Choose the Right Storage Engine

The two main storage engines in MySQL are MyISAM and InnoDB. Each have their own pros and cons.

MyISAM is good for read-heavy applications, but it doesn't scale very well when there are a lot of writes. Even if you are updating one field of one row, the whole table gets locked, and no other process can even read from it until that query is finished. MyISAM is very fast at calculating SELECT COUNT(*) types of queries.

InnoDB tends to be a more complicated storage engine and can be slower than MyISAM for most small applications. But it supports row-based locking, which scales better. It also supports some more advanced

Split the Big DELETE or INSERT Queries

If you need to perform a big DELETE or INSERT query on a live website, you need to be careful not to disturb the web traffic. When a big query like that is performed, it can lock your tables and bring your web application to a halt.

Apache runs many parallel processes/threads. Therefore it works most efficiently when scripts finish executing as soon as possible, so the servers do not experience too many open connections and processes at once that consume resources, especially the memory.

If you end up locking your tables for any extended period of time (like 30 seconds or more), on a high traffic web site, you will cause a process and query pileup, which might take a long time to clear or even crash your web server.

If you have some kind of maintenance script that needs to delete large numbers of rows, just use the LIMIT clause to do it in smaller batches to avoid this congestion.

while (1) {
  mysql_query("DELETE FROM logs WHERE log_date <= '2009-10-01' LIMIT 10000");
  if (mysql_affected_rows() == 0) {
    // done deleting
    break;
  }
  // you can even pause a bit
  usleep(50000);
}

-------------------------------------------------------
Store IP Addresses as UNSIGNED INT

Many programmers will create a VARCHAR(15) field without realizing they can actually store IP addresses as integer values. With an INT you go down to only 4 bytes of space, and have a fixed size field instead.

You have to make sure your column is an UNSIGNED INT, because IP Addresses use the whole range of a 32 bit unsigned integer.

In your queries you can use the INET_ATON() to convert and IP to an integer, and INET_NTOA() for vice versa. There are also similar functions in PHP called ip2long() and long2ip().

$r = "UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id"; 

-------------------------------------------------------
Prepared Statements
There are multiple benefits to using prepared statements, both for performance and security reasons.
Prepared Statements will filter the variables you bind to them by default, which is great for protecting your application against SQL injection attacks. You can of course filter your variables manually too, but those methods are more prone to human error and forgetfulness by the programmer. This is less of an issue when using some kind of framework or ORM.

Since our focus is on performance, I should also mention the benefits in that area. These benefits are more significant when the same query is being used multiple times in your application. You can assign different values to the same prepared statement, yet MySQL will only have to parse it once.

Also latest versions of MySQL transmits prepared statements in a native binary form, which are more efficient and can also help reduce network delays.

There was a time when many programmers used to avoid prepared statements on purpose, for a single important reason. They were not being cached by the MySQL query cache. But since sometime around version 5.1, query caching is supported too.

To use prepared statements in PHP you check out the mysqli extension or use a database abstraction layer like PDO.

-------------------------------------------------------
Unbuffered Queries

Normally when you perform a query from a script, it will wait for the execution of that query to finish before it can continue. You can change that by using unbuffered queries.

There is a great explanation in the PHP docs for the mysql_unbuffered_query() function:

    “mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.”

However, it comes with certain limitations. You have to either read all the rows or call mysql_free_result() before you can perform another query. Also you are not allowed to use mysql_num_rows() or mysql_data_seek() on the result set.
